//SPDX-FileCopyrightText: Copyright 2022-2024 深圳市同心圆网络有限公司
//SPDX-License-Identifier: GPL-3.0-only

package mail_serv

import (
	"context"
	"fmt"
	"io"
	"strings"
	"time"

	"atomgit.com/openlinksaas-org/api-server/api_common"
	"atomgit.com/openlinksaas-org/api-server/dao"
	"atomgit.com/openlinksaas-org/api-server/dao/mail_dao"
	"atomgit.com/openlinksaas-org/proto-gen-go.git/notices/notices_user"
	"github.com/emersion/go-smtp"
	"github.com/mnako/letters"
	"go.mongodb.org/mongo-driver/mongo"
)

type MailBackEnd struct {
	maxMailCount uint32
}

func newMailBackEnd(maxMailCount uint32) *MailBackEnd {
	return &MailBackEnd{maxMailCount: maxMailCount}
}

func (be *MailBackEnd) NewSession(c *smtp.Conn) (smtp.Session, error) {
	return &MailSession{maxMailCount: be.maxMailCount}, nil
}

type MailSession struct {
	maxMailCount uint32
	from         string
	to           *mail_dao.MailAccountDbItem
}

func (s *MailSession) Reset() {
	s.from = ""
	s.to = nil
}

func (s *MailSession) Logout() error {
	return nil
}

func (s *MailSession) Mail(from string, opts *smtp.MailOptions) error {
	s.from = from
	return nil
}

func (s *MailSession) Rcpt(to string, opts *smtp.RcptOptions) error {
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	pos := strings.Index(to, "@")
	if pos == -1 {
		return fmt.Errorf("wrong email")
	}
	account := to[:pos]
	accountItem, err := mail_dao.AccountDao.GetByAccount(ctx, account)
	if err != nil {
		return fmt.Errorf("dest email not exist")
	}
	s.to = accountItem
	return nil
}

func (s *MailSession) Data(r io.Reader) error {
	if s.from == "" || s.to == nil {
		return fmt.Errorf("miss email")
	}
	email, err := letters.ParseEmail(r)
	if err != nil {
		return err
	}
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	dbSess, err := dao.StartSession()
	if err != nil {
		return err
	}
	defer dao.EndSession(dbSess)

	_, err = dbSess.WithTransaction(ctx, func(sessCtx mongo.SessionContext) (interface{}, error) {
		err = mail_dao.MailInfoDao.Insert(sessCtx, &mail_dao.MailInfoDbItem{
			MailId:      string(email.Headers.MessageID),
			UserId:      s.to.UserId,
			FromAccount: s.from,
			ToAccount:   s.to.Account,
			Title:       email.Headers.Subject,
			TimeStamp:   email.Headers.Date.UnixNano() / 1e6,
			HasRead:     false,
		})
		if err != nil {
			return nil, err
		}
		err = mail_dao.MailBodyDao.Insert(sessCtx, &mail_dao.MailBodyDbItem{
			MailId: string(email.Headers.MessageID),
			Text:   email.Text,
			Html:   email.HTML,
		})
		if err != nil {
			return nil, err
		}
		count, err := mail_dao.MailInfoDao.Count(sessCtx, s.to.UserId, false, false)
		if err != nil {
			return nil, err
		}
		if count > s.maxMailCount {
			mailIdList, err := mail_dao.MailInfoDao.ListForRemove(sessCtx, s.to.UserId, int64(count-s.maxMailCount))
			if err != nil {
				return nil, err
			}
			err = mail_dao.MailInfoDao.RemoveById(sessCtx, s.to.UserId, mailIdList)
			if err != nil {
				return nil, err
			}
			err = mail_dao.MailBodyDao.RemoveById(sessCtx, mailIdList)
			if err != nil {
				return nil, err
			}
		}
		return nil, nil
	})

	if err == nil && s.to != nil {
		//发送通知
		go api_common.SendNoticeToUser(context.Background(), s.to.UserId, &notices_user.UserMailNotice{
			UserId: s.to.UserId,
		})
	}
	return err
}
